package roborally.Pieces;

import be.kuleuven.cs.som.annotate.*;
import java.util.*;

import roborally.DijkstraPath;
import roborally.MoveOutOfBoardException;

/**
 * This is a class of Robots which have a position and orientation on a board, an inventory of CarriablePieces 
 * and an amount of energy. Since the maximum amount of energy for each Robot
 * could change in the future (this was indeed the case), the maximum amount of energy and the amount of energy 
 * needed to move, turn and shoot are also ascribed to Robots. 
 *  
 * @note 	Method setPositionWithOrientation(PositionWithOrientation positionWithOrientation) should only
 * 			be used in RoboRally.putRobot, to oblige using methods move(), turnClockwise() and turnCounterClockwise()
 * @note 	In each constructor or method, all aspects related to orientation are
 * 			worked out totally, all aspects related to position defensively and all
 * 			aspects related to energy nominally.
 * @note 	Nominal aspects are specified with preconditions and assert-statements
 * 			which are not meant to be used dynamically
 * @note 	All public methods are specified both formally and informally in this class
 * @note 	Annotation @Raw is not used outside constructors since Robots always satisfy their invariants
 * @note 	Assert-statements that are not executed and some unreachable statements
 * 			or incompatible boolean expressions still give a good code coverage
 * 
 * @invar 	The position is valid 
 * 			| Position.isValidPosition(getPosition().getX(), getPosition().getY(), getPosition().getBoard() )
 * @invar 	The energies are not null
 * 			| getEnergy() != null
 * 			| getMaxEnergy() != null
 * 			| getBasicMoveEnergy() != null
 * 			| getTurnEnergy() != null
 * 			| getShootEnergy() != null
 * @invar 	The energy is valid 
 * 			| isValidEnergy(this.getEnergy(), this.getMaxEnergy() )
 * @invar 	All carried pieces are not placed on the Board
 * 			| forall(CarriablePiece item : getInventory() )
 * 			|	item.getPosition() == null
 * @invar	getMaxEnergy is greater than zero
 * 			| maxEnergy.compareTo(Energy.ZERO) > 0	 
 * 
 * @version 2.0
 * @author Bart Opsomer 2de Bachelor Ingenieurswetenschappen Electrotechniek-Computerwetenschappen 
 * 		   Peter Opsomer 1ste Master Wiskundige IngenieursTechnieken
 */
public class Robot extends GamePieceWithOrientation {
	// the energy of the Robot
	private Energy energy;
	// The maximum amount of energy this Robot can have, is no class variable because it has
	// to be adjustable for each object afterwards
	private final Energy maxEnergy; 
	// The amount of energy needed to turn
	private final Energy turnEnergy; 
	private final Energy shootEnergy;
	// The amount of energy needed to move forward
	private final Energy moveEnergy; 
	// the items the Robot is carrying
	private List<CarriablePiece> inventory;
	/**
	 * Construct a new Robot with a given orientation and a given amount of energy.
	 * Other properties are left default: no given position (Robot is not placed
	 * on any Board) and no CarriablePieces.
	 * 
	 * @pre energy is a valid amount of Energy according to the assignment
	 * 		| isValidEnergy(energy,20000.0)
	 * @param energy the (initial) energy for the Robot
	 * @effect The more extended constructor is called with default arguments 
	 * 		| this(energy, new Energy(20000.0), new Energy(100.0), new Energy(500.0), new Energy(1000));
	 */
	@Raw
	public Robot(Energy energy) throws IllegalArgumentException {
		this(energy, new Energy(20000.0), new Energy(100.0), new Energy(500.0), new Energy(1000));
	}

	/**
	 * Construct a new Robot with a given amount of energy, maximum
	 * amount of energy, energy required to turn and move. The Robot is not
	 * placed on any Board and does not have any CarriablePieces. The 'preconditions' 
	 * maxEnergy.isGreaterThanOrEquals(Energy.ZERO) && turnEnergy.isGreaterThanOrEquals(
	 * Energy.ZERO) && moveEnergy.isGreaterThanOrEquals(Energy.ZERO) 
	 * are automatically satisfied by the class invariant of Energy.
	 * 
	 * 
	 * @pre The energies are not null
	 * 		| energy != null
	 * 		| maxEnergy != null
 	 * 		| moveEnergy != null
  	 * 		| turnEnergy != null
 	 * 		| shootEnergy != null
	 * @pre energy is a valid amount of energy 
	 * 		| isValidEnergy(energy,maxEnergy)
	 * @pre	maxEnergy is greater than zero
	 * 		| maxEnergy.compareTo(Energy.ZERO) > 0	 
	 *  
	 * @param energy the (initial) Energy for the Robot
	 * @param maxEnergy the maximum Energy for the Robot
	 * @param turnEnergy the Energy required to turn for the Robot
	 * @param moveEnergy the Energy required to move for the Robot
	 * 
	 * @effect the more extended constructor of GamePieceWithOrientation is called
	 * 		| super()
	 * @post The energy is set
	 * 		| new.getEnergy().equals(energy)
	 * 	(	| new.getEnergy().compareTo(energy) == 0	) 
	 * @post The maximum energy is set
	 * 		| new.getMaxEnergy().equals(maxEnergy)
	 * @post The amount of energy required to turn is set 
	 * 		| new.getTurnEnergy().equals(turnEnergy)
	 * @post The amount of energy required to move is set
	 * 		| new.getMoveEnergy().equals(moveEnergy)
	 * @post The amount of energy required to shoot is set
	 * 		| new.getShootEnergy().equals(shootEnergy)
	 * @post The inventory of this Robot is empty: it does not have any CarriablePieces
	 * 		| new.getInventory().isEmpty()
	 */
	@Raw
	public Robot(Energy energy, Energy maxEnergy,Energy turnEnergy, Energy moveEnergy, Energy shootEnergy){
		super();
		// Assert-statement is not being used dynamically
		assert (isValidEnergy(energy, maxEnergy));
		// Energy should be OK because of precondition
		assert(maxEnergy.compareTo(Energy.ZERO) > 0);
		this.energy = energy;
		this.maxEnergy = maxEnergy;
		this.turnEnergy = turnEnergy;
		this.moveEnergy = moveEnergy;
		inventory = new ArrayList<CarriablePiece>();
		this.shootEnergy = shootEnergy;
	}

	/**
	 * Test whether the amount of energy is valid. 
	 * energy.isGreaterThanOrEquals(Energy.ZERO) 
	 * is automatically satisfied by the invariant of Energy
	 * 
	 * @param energy the energy to test
	 * @param maxEnergy the maximum energy
	 * @return whether the energy is valid: positive and below maxEnergy 
		|  maxEnergy.isGreaterThanOrEquals(energy)
	 */
	public static boolean isValidEnergy(Energy energy, Energy maxEnergy) {
		return maxEnergy.isGreaterThanOrEquals(energy);
	}
	/**
	 * @return the energy of this Robot
	 */
	@Basic
	public Energy getEnergy() {
		return this.energy;
	}
	/**
	 * @return the maximum amount of energy this Robot can have
	 */
	@Immutable
	@Basic
	public Energy getMaxEnergy() {
		return this.maxEnergy;
	}

	/**
	 * @return the amount of energy needed to turn
	 */
	@Immutable
	@Basic
	private Energy getTurnEnergy() {
		return turnEnergy;
	}
	/**
	 * @return the amount of energy needed to shoot
	 */
	@Immutable
	@Basic
	public Energy getShootEnergy() {
		return shootEnergy;
	}
	/**
	 * @return the amount of energy needed to move
	 */
	@Immutable
	@Basic
	public Energy getBasicMoveEnergy() {
		return moveEnergy;
	}

	/**
	 * Get the energy needed to move one step forward.
	 * This method was already introduced in the first part of
	 * the assignment. Moving a robot consumes 500 Watt-second plus 50
	 * Watt-second per kilogram of items the robot is carrying.
	 * 
	 * @return the amount of energy needed to move one step forward, which is the basic 
	 * amount of energy required to move plus 50 Watt-second per kilogram of items the robot is carrying
	 * 		|	result.compareTo( (new Energy(sum(getInventory().getWeight()*0.05)) ).increase(getBasicMoveEnergy() ) ) == 0
	 */
	private Energy getMoveEnergy() {
		Energy currentMoveEnergy = new Energy(getBasicMoveEnergy() );
		for (CarriablePiece item : inventory) {
			// Weight is expressed in g while 50 Watt-second per kilogram
			currentMoveEnergy.increase(new Energy(item.getWeight() * 0.05));
		}
		return currentMoveEnergy;
	}

	/**
	 * Returns the relative amount of energy
	 * 
	 * @return the fraction of energy that is left expressed in % 
	 *      |   result == getEnergy()/getMaxEnergy()*100
	 */
	public double getRelativeEnergyPercentage() {
		// can't divide by 0 because of invariant getMaxEnergy() > 0
		return 100 * getEnergy().divide(getMaxEnergy());
	}
	/**
	 * The robot's energy is decreased with the given amount.
	 * 
	 * @pre The Robot has at least the amount as energy 
	 * 		| getEnergy().isGreaterThanOrEquals(amount)
	 * @param amount the amount of energy the robot uses
	 * @post The energy of the robot is decreased with the given amount
	 *      | new.getEnergy().compareTo(this.getEnergy().decrease(amount) ) == 0
	 */
	public void useEnergy(Energy amount) {
		if (isTerminated()) {
			return;
		}
		assert (getEnergy().isGreaterThanOrEquals(amount) );
		this.energy.decrease(amount);
	}
	/**
	 * The robot's energy is increased with the given amount.
	 * 
	 * @pre The Robot will not exceed the maximum amount of energy
	 * 		| getMaxEnergy.isGreaterThanOrEquals( getEnergy().increase(amount) ) 
	 * @param amount the amount of energy the robot recharges with
	 * @post The energy of the robot is increased with the given amount
	 *      | new.getEnergy().compareTo(this.getEnergy().increase(amount) ) == 0
	 */
	public void recharge(Energy amount) {
		if (isTerminated()) {
			return;
		}
		Energy temp = new Energy(this.getEnergy());
		temp.increase(amount);
		assert( getMaxEnergy().isGreaterThanOrEquals(temp));
		this.energy.increase(amount);
	}
	/**
	 * Turn this Robot clockwise.
	 * 
	 * @pre The Robot has sufficient energy to rotate
	 * 		| this.getEnergy().isGreaterThanOrEquals(this.getTurnEnergy() )
	 * @post The Robot has turned clockwise
	 * 		| new.getOrientation().equals(this.getOrientation().turnClockWise() )
	 * @effect The Robot has lost the energy required to turn 
	 *      | this.useEnergy(this.getTurnEnergy() )
	 */
	public void turnClockwise() {
		if (!isTerminated()) {
			assert (this.getEnergy()
					.isGreaterThanOrEquals(this.getTurnEnergy()));
			this.positionWithOrientation = positionWithOrientation.turnClockwise();
			useEnergy(getTurnEnergy());
		}
	}
	/**
	 * Turn this Robot counterclockwise.
	 * 
	 * @pre The Robot has sufficient energy to rotate
	 * 		| this.getEnergy().isGreaterThanOrEquals(this.getTurnEnergy() )
	 * @post The Robot has turned counterclockwise
	 * 		| new.getOrientation().equals(this.getOrientation().turnCounterClockWise() )
	 * @effect The Robot has lost the energy required to turn 
	 *      | this.useEnergy(this.getTurnEnergy() )
	 */
	public void turnCounterClockwise() {
		if (!isTerminated()) {
			assert (this.getEnergy()
					.isGreaterThanOrEquals(this.getTurnEnergy()));
			this.positionWithOrientation = positionWithOrientation.turnCounterClockwise();
			useEnergy(getTurnEnergy());
		}
	}
	/**
	 * Move this Robot one step forward. This method should be called from Board so that the Board remains notified of the current Position.
	 * 
	 * @pre The Robot has sufficient energy to move 
	 * 		| this.getEnergy().isGreaterThanOrEquals(this.getMoveEnergy() )
	 * @effect The Robot has moved forward one step 
	 * 		| new.getPositionWithOrientation().equals(this.getPositionWithOrientation().nextPosition() )
	 * @effect The Robot has lost the energy required to move
	 *      | this.useEnergy(this.getMoveEnergy() )
	 * @throws MoveOutOfBoardException
	 * 		This Exception is thrown when the Robot would move outside the Board when moving forward
	 * 		|	let posNext = this.getPositionWithOrientation().nextPosition()
	 * 		|  		in !Position.isValidPosition(posNext.getBoard(), posNext.getX(), posNext.getY() )
	 */
	public void move() throws MoveOutOfBoardException {
		if (!isTerminated()) {
			assert (this.getEnergy().isGreaterThanOrEquals(this.getMoveEnergy()));
			PositionWithOrientation nextPosition = this.positionWithOrientation.nextPosition();
			this.positionWithOrientation = nextPosition;
			useEnergy(getMoveEnergy());
		}
	}

	/**
	 * Calculate the the minimal energy that is needed for this Robot to get to the given Position from
	 * the current position of the robot assuming a turn (in any direction) costs getTurnEnergy() and a 
	 * move forward one step costs getMoveEnergy().
	 * 
	 * @param position the position to reach
	 * @return -1 if this Robot is not places on a Board
	 * 			| if(getPositionWithOrientation() == null) then result == -1.0 
	 * @return -1 if the target Position is not reachable by obstacles, so no combination of adjacent Positions from
	 * 			this Robots position to the target position exists without a Wall or Robot
	 * 			|  if !exists List<PositionWithOrientation> list with
	 * 			|		list.get(0).equals(this.getPosition() ) && list.get(list.size() ).equals(position) &&
	 * 			|		for i = 1..list.size() -1
	 * 			|			(list.get(i).getManhattanDistanceTo(list.get(i-1) ) == 1) &&
	 * 			|			!list.get(i).getBoard().hasRobotAt(list.get(i) ) && 
	 * 			|			!list.get(i).getBoard().hasWallAt(list.get(i) ) 
	 * 			|  then result == -1.0 
	 * @return  -2 if this Robot does not have enough Energy to reach the given Position
	 * 			| if (new Energy( (new Robot(new Energy(Double.MAX_VALUE) ) ).getEnergyRequiredToReach()).isGreaterThanOrEquals(this.getEnergy() )
	 * 			| then result == -2.0 
	 * @return  the minimal energy that is needed to reach the given position, taking into account Walls and other
	 * 			Robots (that cannot move while this method is running) if the other return clauses were not satisfied
	 * 			|  !exists List<PositionWithOrientation> list and Energy energy initially Energy.ZERO with
	 * 			|		list.get(0).equals(this.getPosition() ) && list.get(list.size() ).getPosition().equals(position) &&
	 * 			|		for i = 1..list.size() -1
	 * 			|			(list.get(i).getManhattanDistanceTo(list.get(i-1) ) == 1) &&
	 * 			|			!list.get(i).getBoard().hasRobotAt(list.get(i) ) && 
	 * 			|			!list.get(i).getBoard().hasWallAt(list.get(i) ) 
	 * 			|			energy.increase(getMoveEnergy() )
	 * 			|			energy.increase(new Energy(getTurnEnergy().getAmount()*list.get(i).getOrientation().
	 * 			|				getOrientationDifference(list.get(i-1).getOrientation() ) ) )
	 * 			|  and result < energy.getAmount()
	 * @throws MoveOutOfBoardException
	 * 			This exception is thrown when the given Position is invalid
	 * 			| !isValidPosition(position)
	 * @throws IllegalArgumentException
	 * 			This exception is thrown when this Robot is terminated
	 * 			| this.isTerminated()
	 * @throws IllegalStateException
	 * 			This exception is thrown when the Robot is not on the Board of the given Position
	 * 			|	!position.getBoard().getRobots().contains(this)
	 */
	public double getEnergyRequiredToReach(Position position)
			throws MoveOutOfBoardException,IllegalArgumentException {
		if (!isValidPosition(position)) {
			throw new MoveOutOfBoardException();
		}
		if (isTerminated()) {
			throw new IllegalStateException();
		}
		// this method should return -1 if the robot is not placed on a board
		if(this.positionWithOrientation == null) {
			return -1;
		}
		else if(!position.getBoard().getRobots().contains(this))  {
			throw new IllegalStateException("Robot was not added to board");
		}
		DijkstraPath optimal = DijkstraPath.getOptimalPathTo(getPositionWithOrientation(), position,moveEnergy,turnEnergy);
		// If the robot has insufficient energy to reach the position,this method must return -2.
		if(optimal.getEnergy().isGreaterThanOrEquals(energy)) {
			return -2;
		}
		// also return -1 if the target position is not reachable because of obstacles
		if(optimal.getManhattanDistance() != 0) {
			return -1;
		}
		return optimal.getEnergy().getAmount();
	}

	/**
	 * Move this Robot and/or the given Robot next to each other or as close as
	 * possible. 'robot' should be an instance of Robot
	 * 
	 * @param 	robot  the robot to move next to
	 * 
	 * @post	No endposition exists where the 2 robots are closer to each other, given their start position and energy
	 * 			| let thisPosition = new.getPosition && robotPosition = (new robot).getPosition 
	 * 			| in
	 * 			| 	!exists position1, position2
	 * 			|		where new.getPosition = position1 && (new robot).getPosition = position2 &&
	 * 			|			this.getEnergyRequiredToReach(position1) <= this.getEnergy() &&
	 * 			|			robot.getEnergyRequiredToReach(position2) <= robot.getEnergy() &&
	 * 			|			position1.getManhattanDistanceTo(position2) < thisPosition.getManhattanDistanceTo(robotPosition)
	 * @post    No two path exist for both Robots in which they can get to the same Manhattan Distance in less energy
	 * 			|  !exists List<PositionWithOrientation> list and listRobot and Energy energy initially Energy.ZERO with
	 * 			|		list.get(0).equals(this.getPosition() ) && listRobot.get(0).equals(robot.getPosition() ) &&
	 * 			|		for i = 1..list.size() -1
	 * 			|			(list.get(i).getManhattanDistanceTo(list.get(i-1) ) == 1) &&
	 * 			|			!list.get(i).getBoard().hasRobotAt(list.get(i) ) && 
	 * 			|			!list.get(i).getBoard().hasWallAt(list.get(i) ) 
	 * 			|			energy.increase(getMoveEnergy() )
	 * 			|			energy.increase(new Energy(getTurnEnergy().getAmount()*list.get(i).getOrientation().
	 * 			|				getOrientationDifference(list.get(i-1).getOrientation() ) ) )
	 * 			|		for i = 1..listRobot.size() -1
	 * 			|			(listRobot.get(i).getPosition().getManhattanDistanceTo(listRobot.get(i-1).getPosition() ) == 1) &&
	 * 			|			!listRobot.get(i).getPosition().getBoard().hasRobotAt(listRobot.get(i).getPosition() ) && 
	 * 			|			!listRobot.get(i).getPosition().getBoard().hasWallAt(listRobot.get(i).getPosition() ) 
	 * 			|			energy.increase(robot.getMoveEnergy() )
	 * 			|			energy.increase(new Energy(robot.getTurnEnergy().getAmount()*listRobot.get(i).getOrientation().
	 * 			|				getOrientationDifference(listRobot.get(i-1).getOrientation() ) ) )
	 * 			|  and list.get(list.size()-1).manhattanDistanceTo(listRobot.get(listRobot.size()-1) ) == 
	 * 			|		(new robot).getPosition().getManhattanDistanceTo(new.getPosition() )
	 * 			|  and (new robot).getEnergy().increase(new.getEnergy() ) > energy
	 * 			
	 * @throws 	NullPointerException
	 *         	The destination robot does not exist 
	 *         	| robot == null
	 * @throws 	IllegalArgumentException
	 *         	One of the Robots is not placed on a Board or they are not placed on the same Board
	 *         	|  getPositionWithOrientation() == null || robot.getPositionWithOrientation() == null) ||
	 *          |  (!getPositionWithOrientation().getBoard().equals(robot.getPositionWithOrientation().getBoard() ) 
	 */
	public void moveNextTo(Robot robot) throws IllegalArgumentException, NullPointerException {
		if(getPositionWithOrientation() == null || robot.getPositionWithOrientation() == null) {
			throw new IllegalArgumentException("State of a Robot was invalid: no Position");
		}
		else if(!getPositionWithOrientation().getBoard().equals(
				robot.getPositionWithOrientation().getBoard() ) ) {
			throw new IllegalArgumentException();
		}
		PositionWithOrientation[] start = new PositionWithOrientation[2];
		start[0] = getPositionWithOrientation();
		start[1] = robot.getPositionWithOrientation();
		Energy moveEnergy[] = new Energy[2];
		moveEnergy[0] = getMoveEnergy();
		moveEnergy[1] = robot.getMoveEnergy();
		Energy turnEnergy[] = new Energy[2];
		turnEnergy[0] = getTurnEnergy();
		turnEnergy[1] = robot.getTurnEnergy();
		Energy robotEnergy[] = new Energy[2];
		robotEnergy[0] = getEnergy();
		robotEnergy[1] = robot.getEnergy();
		DijkstraPath[] toExecute = DijkstraPath.getOptimalPathsToReachEachOther(start, moveEnergy, turnEnergy, robotEnergy);
		for(PositionWithOrientation pwo: toExecute[0].getPath()) {
			int orientationDifference = getPositionWithOrientation().getOrientation().getOrientationDifference(pwo.getOrientation() );
			if(orientationDifference == 1) {
				turnClockwise();
			}
			else if(orientationDifference == -1) { 
				turnCounterClockwise();
			}
			else if(orientationDifference == 2) { 
				// Turning counterclockwise should perform way better...
				turnClockwise();
				turnClockwise();
			}
			// don't catch exceptions or test preconditions because DijkstraPath should make sure there is enough energy
			move();
		}
		for(PositionWithOrientation pwo: toExecute[1].getPath()) {
			int orientationDifference = robot.getPositionWithOrientation().getOrientation().getOrientationDifference(pwo.getOrientation() );
			if(orientationDifference == 1) { 
				robot.turnClockwise();
			}
			else if(orientationDifference == -1) { 
				robot.turnCounterClockwise();
			}
			else if(orientationDifference == 2) { 
				robot.turnClockwise();
				robot.turnClockwise();
			}
			robot.move();
		}
	}
	/**
	 * Gives a textual representation of this robot.
	 * 
	 * @return	a textual representation of this robot
	 * 			|  result.equals("Robot with energy percentage " + getRelativeEnergyPercentage() + super.toString() )
	 */
	public String toString() {
		if (!isTerminated()) {
			return "Robot with energy percentage " + getRelativeEnergyPercentage() + super.toString();
		}
		return null;
	}

	/**
	 * Get the i-th heaviest item in constant time. This is done because the
	 * Batteries are in a sorted list: sorted according to their weight.
	 * 
	 * @param i the index number of the i-th heaviest item
	 * @return returns the i-th heaviest item that the robot is carrying
	 * 			| for (j = 1..i)
	 * 			|		getIthHeaviestItem(j).getWeight() >= result.getWeight()
	 * 			| for (j = i..getInventory().size() )
	 * 			|		getIthHeaviestItem(j).getWeight() <= result.getWeight()
	 */
	public CarriablePiece getIthHeaviestItem(int i) {
		if (!isTerminated()) {
			return inventory.get(i-1);
		}
		return null;
	}
	
	/**
	 * Sort the inventory of the robot according to weight, in descending order
	 * 
	 * @post	inventory is sorted on descending weight of the item the robot is carrying
	 * 			| forall i in [0,inventory.size()-1[
	 * 			|    inventory.get(i).getWeight >= inventory.get(i+1).getWeight
	 */
	public void sortBatteryByWeight() {
		if (!isTerminated()) {
			Collections.sort(inventory, new WeightComparator());
		}

	}
	/**
	 * Add the given item to the inventory of the robot
	 * 
	 * @post	the robot contains the given item if it or the robot are not terminated
	 * 			| if( !this.isTerminated || !item.isTerminated() )
	 * 			|	then new.getinventory.contains(item) == true
	 */
	public void addItem(CarriablePiece item) {
		if (!isTerminated()) {
			if(!item.isTerminated()) {
				inventory.add(item);
				sortBatteryByWeight();
			}
		}
	}
	/**
	 * Drops the item on the current position of the robot
	 * @param 	item the item to be removed
	 * @post	the robot does not contain the item 
	 * 			| new.getinventory.contains(item) == false
	 * @post	the position on which the robot is located contains the item
	 * 			| new.getPosition.getBoard.getItemsAt(new.getPosition).contains(item)
	 */
	public void drop(CarriablePiece item) {
		if(!inventory.contains(item)) {
			return;
		}
		inventory.remove(item);
		sortBatteryByWeight();
		if (positionWithOrientation != null) {
			positionWithOrientation.getBoard().addCarriablePiece(getPosition(), item);
		} 
		else {
			// Do nothing: position already was removed from Battery when it was added to Robot
		}
	}
	/**
	 * @return the items the robot is carrying, copied into another Set
	 */
	@Basic
	public Set<CarriablePiece> getInventory() {
		if (!isTerminated()) {
			return new HashSet<CarriablePiece>(inventory);
		}
		return null;
	}
	/**
	 * Returns the batteries the robot is carrying 
	 * @return  the batteries the robot is carrying 
	 * 			| result = HashSet<Battery> batteries
	 * 			|	where forall item where item instanceof Battery
	 * 			|							batteries.contains(item) == true
	 */
	public Set<Battery> getBatteries() {
		if (!isTerminated()) {
			Set<Battery> batteries = new HashSet<Battery>();
			Iterator<CarriablePiece> it = inventory.iterator();
			while (it.hasNext()) {
				CarriablePiece item = it.next();
				if (item instanceof Battery) {
					batteries.add((Battery) item);
				}
			}
			return new HashSet<Battery>(batteries);
		}
		return null;
	}
	/**
	 * Use the item the robot is carrying.
	 * @param item the item to be used
	 * @effect	The item is dropped if it is terminated and used if this is not terminated
	 * 			|  if (item.isTerminated() ) then
	 * 			|		drop(item)
	 * 			|  else if (!this.isTerminated() ) then
	 * 			|		item.use(this)
	 */
	public void use(CarriablePiece item) {
		if(item.isTerminated() ) {
			//"If the battery the robot is trying to use is terminated, the battery must be discarded by the robot"
			drop(item);
		}
		if (!isTerminated() || !item.isTerminated()) {
			item.use(this);
		}
	}
	/**
	 * Terminates this robot and all item it is carrying
	 * @post	This Robot is terminated
	 * 			| new.isTerminated == true
	 * @post	All previous items are terminated
	 * 			| forall items in this.getInventory()
	 * 			|	(new item).isTerminated == true
	 * @post	The inventory is empty
	 * 			| new.getInventory.isEmpty() == true;
	 */
	@Override
	public void terminate() {
		for(CarriablePiece piece : inventory) {
			piece.terminate();
		}
		inventory.removeAll(inventory); 
		super.terminate();
	}
}
